﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Speech.Recognition;
using Microsoft.Speech.AudioFormat;
using Microsoft.Kinect;
using System.Diagnostics;
//using System.Threading;


namespace Kinect.Speech
{
    public class SpeechRecognizer
    {

        private SpeechRecognitionEngine speechRecognizer;

        public event Action<string> OnSpeechDetected;
        public event Action<string> OnSpeechAccepted;
        public event Action<string> OnSpeechTilt;

        public SpeechRecognizer(SpeechRecognitionEngine speechRecognizer)
        {
            this.speechRecognizer = speechRecognizer;
        }

        private static RecognizerInfo GetKinectRecognizer()
        {
            Func<RecognizerInfo, bool> matchingFunc = r =>
            {
                string value;
                r.AdditionalInfo.TryGetValue("Kinect", out value);
                return "True".Equals(value, StringComparison.InvariantCultureIgnoreCase) && "en-GB".Equals(r.Culture.Name, StringComparison.InvariantCultureIgnoreCase);
            };
            return SpeechRecognitionEngine.InstalledRecognizers().Where(matchingFunc).FirstOrDefault();
        }

        public SpeechRecognitionEngine CreateSpeechRecognizer()
        {
            RecognizerInfo ri = GetKinectRecognizer();

            if (ri == null)
            {
                Debug.WriteLine("There was a problem initializing Speech Recognition. Failed to load Speech SDK");
                return null;
            }

            SpeechRecognitionEngine sre;
            try
            {
                sre = new SpeechRecognitionEngine(ri.Id);
            }
            catch
            {
                Debug.WriteLine("There was a problem initializing Speech Recognition. Failed to load Speech SDK");
                return null;
            }

            var grammar = new Choices();
            grammar.Add("start");
            grammar.Add("break");
            grammar.Add("stop");
            grammar.Add("step into");
            grammar.Add("step out");
            grammar.Add("step over");

            grammar.Add("kinect up");
            grammar.Add("kinect down");

            var gb = new GrammarBuilder { Culture = ri.Culture };
            gb.Append(grammar);

            // Create the actual Grammar instance, and then load it into the speech recognizer.
            var g = new Grammar(gb);

            sre.LoadGrammar(g);
            sre.SpeechRecognized += this.SreSpeechRecognized;
            sre.SpeechHypothesized += this.SreSpeechHypothesized;
            sre.SpeechRecognitionRejected += this.SreSpeechRecognitionRejected;

            return sre;
        }

        private void RejectSpeech(RecognitionResult result)
        {
            string status = "Rejected: " + (result == null ? string.Empty : result.Text + " " + result.Confidence.ToString("F"));
            OnSpeechDetected(status);
        }

        private void SreSpeechRecognitionRejected(object sender, SpeechRecognitionRejectedEventArgs e)
        {
            this.RejectSpeech(e.Result);
        }

        private void SreSpeechHypothesized(object sender, SpeechHypothesizedEventArgs e)
        {
            string status = "Hypothesized: " + e.Result.Text + " " + e.Result.Confidence.ToString("F");
        }

        private void SreSpeechRecognized(object sender, SpeechRecognizedEventArgs e)
        {
            //Accept only speech that has confidence at least 0.5
            if (e.Result.Confidence < 0.5)
            {
                this.RejectSpeech(e.Result);
                return;
            }

            //If it is a TILT command then trigger the corresponding event, otherwise go to mapping unit
            if (e.Result.Text.ToUpperInvariant() == "KINECT UP" || e.Result.Text.ToUpperInvariant() == "KINECT DOWN")
            {
                OnSpeechTilt(e.Result.Text.ToUpperInvariant());
            }
            else
            {
                OnSpeechAccepted(e.Result.Text.ToUpperInvariant());
            }

            string status = "Recognized: " + e.Result.Text + " " + e.Result.Confidence.ToString("F");
            OnSpeechDetected(status);
        }

        private void UpdateInstructionsText(string instructions)
        {
            //Dispatcher.BeginInvoke(new Action(() => { tbColor.Text = instructions; }), DispatcherPriority.Normal);
        }



        //private void EnableAecChecked(object sender, RoutedEventArgs e)
        //{
        //    CheckBox enableAecCheckBox = (CheckBox)sender;
        //    if (enableAecCheckBox.IsChecked != null)
        //    {
        //        this.kinect.AudioSource.EchoCancellationMode = enableAecCheckBox.IsChecked.Value
        //                                                     ? EchoCancellationMode.CancellationAndSuppression
        //                                                     : EchoCancellationMode.None;
        //    }
        //}

        
    }
}
